package com.sunlei.seata.bussiness.balancer;

import com.alibaba.nacos.client.naming.utils.CollectionUtils;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.*;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;

import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.servlet.http.HttpServletRequest;
import java.net.URI;
import java.util.*;

@Log4j2
public class GrayLoadBalancer implements ReactorServiceInstanceLoadBalancer {

    private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
    private String serviceId;

    public GrayLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
        this.serviceId = serviceId;
        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
    }

    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
//        HttpHeaders headers = (HttpHeaders) request.getContext();

        RequestDataContext context = (RequestDataContext) request.getContext();
        RequestData clientRequest = context.getClientRequest();

        HttpHeaders headers = clientRequest.getHeaders();

        if (this.serviceInstanceListSupplierProvider != null) {
            ServiceInstanceListSupplier supplier = this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
            return supplier.get().next().map(item -> getInstanceResponse(item, headers));
        }
        return null;
    }


    private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances, HttpHeaders headers) {
        System.out.println("获取服务实例：");
        if (instances.isEmpty()) {
            return getServiceInstanceEmptyResponse();
        }
        return getServiceInstanceResponseByUidsOrGrayTag(instances, headers);
    }

    /**
     * 从nacos获取服务实例列表，并根据策略返回灰度服务的实例还是正常服务的实例
     */
    private Response<ServiceInstance> getServiceInstanceResponseByUidsOrGrayTag(List<ServiceInstance> instances, HttpHeaders headers) {
        List<ServiceInstance> grayInstances = new ArrayList<>();
        List<ServiceInstance> normalInstances = new ArrayList<>();

        for (ServiceInstance instance : instances) {
            Map<String, String> metadata = instance.getMetadata();
            // nacos元数据包含“gray-tag”的key值,且value="true"，则判定为灰度实例
            System.out.println("实例版本：" + metadata.get("version"));
            System.out.println("实例：" + metadata);
            String isGrayInstance = metadata.get("version");
            if ("2.0".equals(isGrayInstance)) {
                grayInstances.add(instance);
            } else {
                normalInstances.add(instance);
            }
        }
        //没有灰度服务，直接返回
        if (grayInstances.isEmpty()) {
            return new DefaultResponse(chooseOneInstance(normalInstances));
        }
        //有灰度服务，判断是否需要灰度
        if (checkIfNeedGray(headers)) {
            log.info("gray service of {} will be called", this.serviceId);
            return new DefaultResponse(chooseOneInstance(grayInstances));
        }
        return new DefaultResponse(chooseOneInstance(normalInstances));
    }


    private ServiceInstance chooseOneInstance(List<ServiceInstance> serviceInstances) {

        int size = serviceInstances.size();
        if (size == 1) {
            return serviceInstances.get(0);
        }
        Random rand = new Random();
        int random = rand.nextInt(size);
        return serviceInstances.get(random);
    }

    /**
     * 灰度判断逻辑：
     */
    private boolean checkIfNeedGray(HttpHeaders headers) {
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        if(Objects.isNull(ra)){
            log.info("服务里RequestAttributes对象为空");
            return false;
        }
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();
        String grayTag = request.getHeader("version");

        if (grayTag != null) {
            if ("2.0".equals(grayTag)) {
                return true;
            }
        }
        return false;
    }

    private Response<ServiceInstance> getServiceInstanceEmptyResponse() {
        log.warn("No servers available for service: " + this.serviceId);
        return new EmptyResponse();
    }
}

